﻿using System;
using System.Collections.Generic;
using MEFAddin.Adapters;
using MEFAddin.Interfaces;

namespace MEFAddin
{
    /// <summary>
    /// This object represents multiple controllers
    /// </summary>
    public class MEFControllerChain : IAddInController
    {
        List<IAddInController> _Controllers;
        private IAddInAdapter _AddinAdapter;
        private AddInContext _AddinCtx;

        /// <summary>
        /// Initializes a new instance of the <see cref="MEFControllerChain"/> class.
        /// </summary>
        public MEFControllerChain()
        {
            _Controllers = new List<IAddInController>();
        }

        /// <summary>
        /// Registers the specified controller.
        /// </summary>
        /// <param name="controller">The controller.</param>
        /// <returns></returns>
        public MEFControllerChain Register(IAddInController controller)
        {
            _Controllers.Add(controller);
            return this;
        }

        /// <summary>
        /// Gets the add in adapter.
        /// </summary>
        public IAddInAdapter AddInAdapter
        {
            get { return _AddinAdapter; }
        }

        /// <summary>
        /// Gets the command events.
        /// </summary>
        public EnvDTE.CommandEvents CommandEvents
        {
            get { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Execs the specified CMD name.
        /// </summary>
        /// <param name="CmdName">Name of the CMD.</param>
        /// <param name="ExecuteOption">The execute option.</param>
        /// <param name="VariantIn">The variant in.</param>
        /// <param name="VariantOut">The variant out.</param>
        /// <param name="Handled">if set to <c>true</c> [handled].</param>
        public void Exec(string CmdName, EnvDTE.vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.Exec(CmdName, ExecuteOption, ref VariantIn, ref VariantOut, ref Handled);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when [add ins update].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnAddInsUpdate(ref Array custom)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.OnAddInsUpdate(ref custom);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when [begin shutdown].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnBeginShutdown(ref Array custom)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.OnBeginShutdown(ref custom);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when [connection].
        /// </summary>
        /// <param name="application">The application.</param>
        /// <param name="connectMode">The connect mode.</param>
        /// <param name="addInInst">The add in inst.</param>
        /// <param name="custom">The custom.</param>
        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.OnConnection(application, connectMode, addInInst, ref custom);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when [disconnection].
        /// </summary>
        /// <param name="disconnectMode">The disconnect mode.</param>
        /// <param name="custom">The custom.</param>
        public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref Array custom)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.OnDisconnection(disconnectMode, ref custom);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when [startup complete].
        /// </summary>
        /// <param name="custom">The custom.</param>
        public void OnStartupComplete(ref Array custom)
        {
            foreach (var controller in this._Controllers)
            {

                try
                {
                    controller.OnStartupComplete(ref custom);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Queries the status.
        /// </summary>
        /// <param name="CmdName">Name of the CMD.</param>
        /// <param name="NeededText">The needed text.</param>
        /// <param name="StatusOption">The status option.</param>
        /// <param name="CommandText">The command text.</param>
        public void QueryStatus(string CmdName, EnvDTE.vsCommandStatusTextWanted NeededText, ref EnvDTE.vsCommandStatus StatusOption, ref object CommandText)
        {
            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.QueryStatus(CmdName, NeededText, ref StatusOption, ref CommandText);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Initializes the specified addin CTX.
        /// </summary>
        /// <param name="addinCtx">The addin CTX.</param>
        /// <param name="addinAdapter">The addin adapter.</param>
        public void Initialize(AddInContext addinCtx, IAddInAdapter addinAdapter)
        {
            this._AddinCtx = addinCtx;
            this._AddinAdapter = addinAdapter;

            foreach (var controller in this._Controllers)
            {
                try
                {
                    controller.Initialize(addinCtx, addinAdapter);
                }
                catch (Exception e)
                {
                    LogExeption(controller, e);
                    throw;
                }
            }
        }

        private void LogExeption(IAddInController controller, Exception e)
        {
            this._AddinAdapter.LoggingAdapter.LogError(string.Format("Controller [{0}] Exception: {1}", controller.GetType().FullName, e.ToString()));
        }
    }
}
